/*
 * Copyright 2009 Andrey Khalzov, and individual contributors as indicated by the @author tag.
 *
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 *      http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing,
 * software distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and limitations under the License.
 */

package org.wannatrak.test.simulator;

import org.apache.log4j.Logger;
import org.wannatrak.GpsData;
import org.wannatrak.test.client.Client;
import org.wannatrak.test.client.TcpClient;
import org.wannatrak.test.encoder.GpsDataEncoder;
import org.wannatrak.test.encoder.sample.SampleGpsDataEncoder;
import org.wannatrak.test.generator.GpsDataGenerator;
import org.wannatrak.test.generator.random.RandomGpsDataGenerator;

import java.io.IOException;
import java.util.Date;

/**
 * Created 14.12.2009 0:25:34
 *
 * @author Andrey Khalzov
 */
public class Simulator {

    private static final Logger logger = Logger.getLogger(Simulator.class);

    private final Client client;
    private final GpsDataEncoder gpsDataEncoder;
    private final GpsDataGenerator gpsDataGenerator;
    private final int duration;
    private final int timeout;

    private long maxResponseTime = 0;
    private long minResponseTime = Long.MAX_VALUE;
    private long averageResponseTime;

    private long numOfDeliveredPackets;
    private long numOfLostPackets;

    public Simulator(
            Client client,
            GpsDataEncoder gpsDataEncoder,
            GpsDataGenerator gpsDataGenerator,
            int duration,
            int frequency
    ) {
        this.client = client;
        this.gpsDataEncoder = gpsDataEncoder;
        this.gpsDataGenerator = gpsDataGenerator;
        this.duration = duration * 1000;
        this.timeout = 1000 / frequency;
    }

    public long getMaxResponseTime() {
        return maxResponseTime;
    }

    public long getMinResponseTime() {
        return minResponseTime;
    }

    public long getAverageResponseTime() {
        return averageResponseTime;
    }

    public long getNumOfDeliveredPackets() {
        return numOfDeliveredPackets;
    }

    public long getNumOfLostPackets() {
        return numOfLostPackets;
    }

    public long getTotalSentPackets() {
        return numOfDeliveredPackets + numOfLostPackets;
    }

    public void simulate() {
        logger.info("Estimated number of simulated packets: " + duration / timeout);

        final long start = System.currentTimeMillis();
        logger.info("Simulation started at " + new Date());

        while ((System.currentTimeMillis() - start) < duration) {
            try {
                final GpsData gpsData = gpsDataGenerator.next();

                final byte[] packet = gpsDataEncoder.encode(gpsData);

                final long startSendingTs = System.currentTimeMillis();

                final byte[] response = client.send(packet);

                final long responseTime = System.currentTimeMillis() - startSendingTs;
                if (responseTime > maxResponseTime) {
                    maxResponseTime = responseTime;
                }
                if (responseTime < minResponseTime) {
                    minResponseTime = responseTime;
                }
                averageResponseTime += responseTime;

                numOfDeliveredPackets++;

                logger.info(new String(response));
                Thread.sleep(timeout);
            } catch (Exception e) {
                numOfLostPackets++;
                logger.error("Simulate error: ", e);
            }
        }

        averageResponseTime /= getTotalSentPackets();
    }

    public static void main(String[] args) throws IOException, InterruptedException {

        String host = "localhost";
        int port = 31031;
        int duration = 10;
        int frequency = 16;
        String id = "mobile";
        String type = "mobile";
        int bufferSize = 10;
        for (int i = 0; i < args.length; i++) {
            if ("-h".equals(args[i]) || "-host".equals(args[i])) {
                host = args[++i];
            } else if ("-p".equals(args[i]) || "-port".equals(args[i])) {
                port = Integer.parseInt(args[++i]);
            } else if ("-d".equals(args[i]) || "-duration".equals(args[i])) {
                duration = Integer.parseInt(args[++i]);
            } else if ("-f".equals(args[i]) || "-frequency".equals(args[i])) {
                frequency = Integer.parseInt(args[++i]);
            } else if ("-i".equals(args[i]) || "-id".equals(args[i])) {
                id = args[++i];
            } else if ("-t".equals(args[i]) || "-type".equals(args[i])) {
                type = args[++i];
            } else if ("-b".equals(args[i]) || "-buffer".equals(args[i])) {
                bufferSize = Integer.parseInt(args[++i]);
            }
        }

        final TcpClient tcpClient = new TcpClient(host, port, bufferSize);
        final GpsDataEncoder gpsDataEncoder = new SampleGpsDataEncoder();
        final GpsDataGenerator gpsDataGenerator = new RandomGpsDataGenerator(id, type);

        final Simulator simulator = new Simulator(tcpClient, gpsDataEncoder, gpsDataGenerator, duration, frequency);
        simulator.simulate();

        logger.info("Total sent: " + simulator.getTotalSentPackets());
        logger.info("Delivered: " + simulator.getNumOfDeliveredPackets());
        logger.info("Lost: " + simulator.getNumOfLostPackets());
        logger.info("Max. response time: " + simulator.getMaxResponseTime() + " ms");
        logger.info("Min. response time: " + simulator.getMinResponseTime() + " ms");
        logger.info("Avg. response time: " + simulator.getAverageResponseTime() + " ms");
    }
}
